package ar.posti.framework.query.core.parameters;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import ar.posti.framework.query.core.VariableExpression;



public class ParameterContainer implements VariableExpression {
	private Set<String> declaredParameters;
//	private Set constants;
    private TreeMap<String, Object> variables;
    private int paramCounter;

    public ParameterContainer() {
    	this.declaredParameters = new HashSet<String>();
//    	this.constants = new HashSet();
        this.variables = new TreeMap<String, Object>();
        this.paramCounter = 0;
    }
    
    public Map<String, Object> getVariables() {
        return variables;
    }
    
    public Object getParameterValue(String parameter) {
        return 	this.isVariable(parameter)? 
        			this.getVariable(parameter) : 
        			this.getConstant(parameter);
    }

    public boolean isInitialized(String parameter) {
    	return 	this.isVariable(parameter)? 
    				this.isInitializedVariable(parameter.substring(1)) : 
    			    this.isInitializedConstant(parameter);
    }    /**
	 * @param parameter
	 * @return
	 */
	private boolean isInitializedConstant(String parameter) {
		return true;
	}

/**
	 * @param parameter
	 * @return
	 */
	private boolean isInitializedVariable(String variable) {
		return this.variables.containsKey(variable);
	}

	
	private Object getConstant(String constant) {
		if (this.isDeclared(constant))
			return constant;
		else
			throw new RuntimeException("la constante " + constant + " no fue declarada"); //TODO AFP: Manejo de excepciones
	}

	private Object getVariable(String parameter) {
		String variable = parameter.substring(1);
		if (!this.isDeclaredVariable(variable))
			throw new RuntimeException("la variable " + variable + " no fue declarada"); //TODO AFP: Manejo de excepciones
		if (this.isInitializedVariable(variable))
			return this.variables.get(variable);
		else
			throw new RuntimeException("la variable " + variable + " no fue inicializada"); //TODO AFP: Manejo de excepciones
	}

	/**
	 * @param parameter
	 * @return
	 */
	public boolean isDeclared(String parameter) {
		return this.declaredParameters.contains(parameter);
	}
    
	public boolean isVariable(String parameter) {
		return parameter.startsWith(":");
	}

	public void bindVariable(String parameter, Object value) {
		this.declareParameter(":" + parameter);
        this.variables.put(parameter, value);
    }
    
    public String declareParameter() {
        String parameter = this.newParameterName();
        return this.declareParameter(parameter);
    }
    
    public String declareParameter(String parameter) {
    	this.declaredParameters.add(parameter);
    	return parameter;
//        return 	this.isVariable(parameter)?
//        			this.declareVariable(parameter) :
//        			this.declareConstant(parameter);
    }
    
//	private String declareConstant(String parameter) {
//		String constant = parameter.substring(1);
//		this.constants.add(constant);
//		return constant;
//	}
//
//	private String declareVariable(String variable) {
//	}

    private String newParameterName() {
        //para garantizar que no haya dos parametros con el mismo nombre al
        //mismo tiempo, utilizamos la posicion de memoria de este objeto mas
        //un contador de la cantidad de paremetros
        return "param_" + Integer.toHexString(super.hashCode() + paramCounter); 
    }

	public void unbindVariable(String variable) {
		if (!this.isDeclaredVariable(variable))
			throw new RuntimeException("la variable " + variable + " no fue declarada"); //TODO AFP: Manejo de excepciones

		this.declaredParameters.remove(":" + variable);
		this.variables.remove(variable);
	}

	/**
	 * @param parameter
	 * @return
	 */
	public boolean isDeclaredVariable(String parameter) {
		return 	this.isDeclared(":" + parameter);
	}

	/**
	 * @param string
	 * @return
	 */
	public String getParameterDeclaration(String parameter) {
		if (this.isDeclared(parameter))
			return parameter;
		else
			throw new RuntimeException("el parametro " + parameter + " no fue declarado");
	}
}
